package com.ccc.netty.server.heart.handler;

import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleStateEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 自定义解包处理器
 *
 * @author superC
 * @version 1.0.0
 * @date 2022/8/17
 */

@ChannelHandler.Sharable
public class HeartDataHandler extends ChannelInboundHandlerAdapter {
    protected static final Logger logger = LoggerFactory.getLogger(HeartDataHandler.class);

    /**
     * 最大读超时次数，超过该值关闭连接
     */
    public static final int MAX_READ_IDLE_TIMES = 3;

    /**
     * 读超时次数集合（通信通道，读超时次数）
     */
    private final Map<ChannelHandlerContext, Integer> readIdleTimesMap = new ConcurrentHashMap<>();

    /**
     * 设备通信通道集合（通信通道，设备编号）
     */
    private final Map<ChannelHandlerContext, String> deviceChannelMap = new ConcurrentHashMap<>();

    /**
     * 表示 channel 处于就绪状态, 建立连接
     *
     * @param ctx
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        logger.info("【Netty-Server（splitpacket）】——客户端建立连接，客户端地址为{} ，ctx={}", ctx.channel().remoteAddress(), ctx);
    }

    /**
     * 表示 channel 处于不活动状态, 断开连接
     *
     * @param ctx
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        logger.warn("【Netty-Server（splitpacket）】——客户端断开连接，客户端地址为{} ，ctx={}", ctx.channel().remoteAddress(), ctx);
        // 设备离线处理
        this.deviceIsOffLine(ctx);
    }

    /**
     * 设备离线处理
     *
     * @param ctx
     */
    private void deviceIsOffLine(ChannelHandlerContext ctx) {
        String deviceCode = deviceChannelMap.get(ctx);
        logger.error("当前设备已【离线】,设备编号为{},设备ip地址为{}", deviceCode, ctx.channel().remoteAddress());
        if (deviceCode == null) {
            logger.error("【LE-Netty-Server（ZLF）】——获取设备编号为空！");
            return;
        }

        /*
         离线处理
         */
        // 离线处理代码

        /*
        清除map数据
         */
        readIdleTimesMap.remove(ctx);
        deviceChannelMap.remove(ctx);

    }
    /**
     * 读取客户端发送的数据
     *
     * @param ctx
     * @param msg 客户端发送数据
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        logger.info("-------------channel read start-----------");

        // 解析报文，获取设备编号，此次代码根据实际业务需求来。
        // 目前的代码测试时，client就发送一个deviceCode。
        String deviceCode = (String) msg;
        logger.error("获取设备编号为{}", deviceCode);

        if (StringUtils.isEmpty(deviceChannelMap.get(ctx))) {
            deviceChannelMap.put(ctx, deviceCode);
        }
        logger.info("-------------channel read end-----------");
    }

    /**
     * Netty心跳检测处理——设备断网离线监测
     *
     * @param ctx
     * @param evt
     * @throws Exception
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        // 判断evt是否是IdleStateEvent（用于触发用户事件，包含 读空闲/写空闲/读写空闲）
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;

            Integer readIdleTimes = readIdleTimesMap.get(ctx);
            if (readIdleTimes == null) {
                readIdleTimes = 0;
            }

            String eventType = null;
            switch (event.state()) {
                case READER_IDLE:
                    eventType = "读空闲";
                    // 读空闲的计数加1
                    readIdleTimes++;
                    readIdleTimesMap.put(ctx, readIdleTimes);
                    break;
                case WRITER_IDLE:
                case ALL_IDLE:
                default:
                    // 不处理
                    break;
            }
            logger.warn("【LE-Netty-Server（ZLF）】——超时事件，客户端地址为{} ，事件为{}", ctx.channel().remoteAddress(), eventType);
            if (readIdleTimes > MAX_READ_IDLE_TIMES) {
                logger.error("【LE-Netty-Server（ZLF）】——读空闲超过{}次，判定设备已【超时离线】，手动断开连接", MAX_READ_IDLE_TIMES);
                // 手动断开连接
                ctx.channel().close();
            }
        }
    }


    /**
     * 接收客户端发送的消息完毕
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    /**
     * 异常处理
     *
     * @param ctx
     * @param cause
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        logger.error("【Netty-Server（splitpacket）】——程序异常，客户端地址为{} ，ctx={}", ctx.channel().remoteAddress(), ctx);
        logger.error("【捕获异常-Netty异常】\r\n异常记录：", cause);
        // 关闭socket通道
        ctx.close();
    }

}